#include <ximera/xml/xmldocument.hpp>
#include <ximera/common/utils.hpp>

#include <ximera/xml/xmliterator.hpp>

#include <ximera/xml/xmlelement.hpp>
#include <ximera/xml/xmltext.hpp>
#include <ximera/xml/xmldeclaration.hpp>
#include <ximera/xml/xmlcomment.hpp>

#include <tinyxml.h>

namespace ximera { namespace xml
{
    String properString(char const* str)
    {
        if (!str)
            return L"";

        return string_util::str2wstr(str);
    }

    void XmlDocument::load(String const& fileName)
    {
        TiXmlDocument tiDoc(string_util::wstr2str(fileName).c_str());
        tiDoc.LoadFile();
        load(tiDoc);
    }

    void XmlDocument::loadXml(String const& xmlStr)
    {
        TiXmlDocument tiDoc("ximera");
        tiDoc.Parse(string_util::wstr2str(xmlStr).c_str());
        load(tiDoc);
    }

    XmlElement* XmlDocument::rootElement() const
    {
        XmlIterator<XmlElement> element(this);
        return element ? element.get() : 0;
    }

    void XmlDocument::load(TiXmlDocument& tiDoc)
    {
        if (tiDoc.Error())
            XIMERA_THROW(XmlParserException, string_util::str2wstr(tiDoc.ErrorDesc()));

        if (!tiDoc.FirstChildElement())
            XIMERA_THROW(XmlParserException, L"Document does not contain a root element!");

        value(properString(tiDoc.Value()));

        TiXmlNode* tiChild = tiDoc.FirstChild();
        while (tiChild)
        {
            load(this, *tiChild);
            tiChild = tiChild->NextSibling();
        }
    }

    void XmlDocument::load(XmlNode* parent, TiXmlNode& tiNode)
    {
        XmlNode* child = 0;

        if (TiXmlElement* tiElement = tiNode.ToElement())
        {
            XmlElement* element = new XmlElement();
            child = element;
            
            TiXmlAttribute* attr = tiElement->FirstAttribute();            
            while (attr)
            {
                element->attribute(properString(attr->Name()), properString(attr->Value()));
                attr = attr->Next();
            }
        }
        else if (TiXmlText* tiText = tiNode.ToText())
        {
            child = new XmlText();
        }
        else if (TiXmlDeclaration* tiDecl = tiNode.ToDeclaration())
        {
            XmlDeclaration* decl = new XmlDeclaration();
            child = decl;

            decl->_version = properString(tiDecl->Version());
            decl->_encoding = properString(tiDecl->Encoding());
            decl->_standalone = properString(tiDecl->Standalone());
        }
        else if (TiXmlComment* tiComment = tiNode.ToComment())
        {
            child = new XmlComment();
        }
        else
        {
            child = new XmlNode();
        }

        child->value(properString(tiNode.Value()));

        parent->insert(child);

        TiXmlNode* tiChild = tiNode.FirstChild();
        while (tiChild)
        {
            load(child, *tiChild);
            tiChild = tiChild->NextSibling();
        }
    }
}}